/**
 * @file pt_ui_pageman.c
 * @brief 
 * @author yejiahao
 * @date 2023-11-07 18:38:54
 * 
 * @copyright Copyright (c) 2023 by 深圳市鹏天微智能科技有限公司, All Rights Reserved.
 */

#include "pt_ui_pageman.h"

typedef struct
{
    pt_list_t node;
    int topic_id;
    pt_mbus_callback_t cbk;
    void *user_data;
} pt_ui_page_mbus_t;

static uint32_t pt_ui_page_global_uid = 1; // 从1开始计数，0不使用
static pt_list_t pt_ui_pages;
static pt_list_t pt_ui_page_subscribers;

extern lv_indev_t *pt_get_lv_keypad_indev(void);
extern lv_obj_t *pt_ui_lvgl_create_screen(lv_obj_t *);

static void pt_ui_unload_page(pt_list_t *n)
{
    pt_list_remove(n);

    struct pt_ui_page_info *page = pt_container_of(n, struct pt_ui_page_info, node);
    if(page->op->destroy) {
        page->op->destroy(&page->ctx);
    }

    pt_list_t *pos;
    pt_ui_widget_info_t *widget;
    pt_list_for_each_safe(pos, n, &page->ctx.widgets) {
        widget = pt_container_of(pos, pt_ui_widget_info_t, node);
        if(widget->op->destroy) {
            widget->op->destroy(&widget->ctx);
        }
        pt_list_remove(pos);
        pt_free(widget);
    }

    pt_ui_page_mbus_t *subscriber;
    pt_list_for_each_safe(pos, n, &pt_ui_page_subscribers) {
        subscriber = pt_container_of(pos, pt_ui_page_mbus_t, node);
        PT_LOGI("[pt_ui_unload_page]: pt_mbus_unsubscribe 0x%08x", subscriber->cbk);
        pt_mbus_unsubscribe(subscriber->topic_id, subscriber->cbk, subscriber->user_data);
        pt_list_remove(pos);
        pt_free(subscriber);
    }
    
    lv_group_del(page->group);
    lv_obj_del_async(page->ctx.page_root);
    pt_free(page);
}

static void pt_ui_suspend_page(pt_list_t *n)
{
    struct pt_ui_page_info *page = pt_container_of(n, struct pt_ui_page_info, node);
    if(page->op->will_disappear) {
        page->op->will_disappear(&page->ctx);
    }

    pt_list_t *pos;
    pt_ui_widget_info_t *widget;
    pt_list_for_each_safe(pos, n, &page->ctx.widgets) {
        widget = pt_container_of(pos, pt_ui_widget_info_t, node);
        if(widget->op->will_disappear) {
            widget->op->will_disappear(&widget->ctx);
        }
    }

    pt_ui_page_mbus_t *subscriber;
    pt_list_for_each_safe(pos, n, &pt_ui_page_subscribers) {
        subscriber = pt_container_of(pos, pt_ui_page_mbus_t, node);
        PT_LOGI("[pt_ui_suspend_page]: pt_mbus_unsubscribe 0x%08x", subscriber->cbk);
        pt_mbus_unsubscribe(subscriber->topic_id, subscriber->cbk, subscriber->user_data);
        pt_list_remove(pos);
        pt_free(subscriber);
    }
}

static void pt_ui_resume_page(pt_list_t *n)
{
    struct pt_ui_page_info *page = pt_container_of(n, struct pt_ui_page_info, node);

    if(page->op->will_appear) {
        page->op->will_appear(&page->ctx);
    }

    pt_list_t *pos;
    pt_ui_widget_info_t *widget;
    pt_list_for_each_safe(pos, n, &page->ctx.widgets) {
        widget = pt_container_of(pos, pt_ui_widget_info_t, node);
        if(widget->op->will_appear) {
            widget->op->will_appear(&widget->ctx);
        }
    }

    lv_group_set_default(page->group);
    lv_indev_set_group(pt_get_lv_keypad_indev(), page->group);

    lv_scr_load(page->ctx.page_root);
}

void pt_ui_page_init(void)
{   
    pt_list_init(&pt_ui_pages);
    pt_list_init(&pt_ui_page_subscribers);
}

uint32_t pt_ui_page_load(const struct pt_ui_page_operation *op, const void *arg)
{
    struct pt_ui_page_info *page = pt_calloc(1, sizeof(struct pt_ui_page_info) + op->user_data_size);
    page->op = op;
    if(op->user_data_size != 0) {
        page->ctx.user_data = ((uint8_t *)page) + sizeof(struct pt_ui_page_info);
    }
    
    // 挂起最后一个页面
    if(!pt_list_isempty(&pt_ui_pages)) {
        pt_ui_suspend_page(pt_ui_pages.prev);
    }

    // 载入新的页面
    pt_list_insert_before(&pt_ui_pages, &page->node);
    page->group = lv_group_create();
    lv_group_set_default(page->group);
    pt_list_init(&page->ctx.widgets);

    page->page_uid = pt_ui_page_global_uid++;

    page->ctx.page_root = pt_ui_lvgl_create_screen(NULL);
    op->create(&page->ctx, (void *)arg);

    pt_ui_resume_page(&page->node);

    PT_LOGI("[pt_ui_page_load]: page_uid %d op 0x%08x arg 0x%08x", page->page_uid, op, arg);

    return page->page_uid;
}

void pt_ui_page_goback(void)
{
    if(pt_list_len(&pt_ui_pages) <= 1) {
        PT_LOGE("pt_ui_page_goback: pages empty or only root");
        return;
    }

    pt_ui_unload_page(pt_ui_pages.prev);
    pt_ui_resume_page(pt_ui_pages.prev);
}

void pt_ui_page_goback_root(void)
{
    pt_list_t *pos, *n;
    pt_list_t *page_root = pt_ui_pages.next;

    if(pt_list_len(&pt_ui_pages) <= 1) {
        PT_LOGE("pt_ui_page_goback_root: pages empty or only root");
        return;
    }

    pt_list_remove(page_root);

    pt_list_for_each_safe(pos, n, &pt_ui_pages) {
        pt_ui_unload_page(pos);
    }

    pt_list_insert_after(&pt_ui_pages, page_root);
    pt_ui_resume_page(page_root);
}

void pt_ui_page_load_widget(struct pt_ui_page_context* ctx, const struct pt_ui_page_operation *widget_op, void *arg)
{
    pt_ui_widget_info_t *widget = pt_calloc(1, sizeof(pt_ui_widget_info_t) + widget_op->user_data_size);
    widget->op = widget_op;
    widget->ctx.page_root = ctx->page_root;
    if(widget_op->user_data_size != 0) {
        widget->ctx.user_data = ((uint8_t *)widget) + sizeof(pt_ui_widget_info_t);
    }

    // 插入widget列表末尾
    pt_list_insert_before(&ctx->widgets, &widget->node);

    widget->op->create(&widget->ctx, arg);
}

void pt_ui_page_subscribe(int topic_id, pt_mbus_callback_t cbk, void *user_data)
{
    pt_ui_page_mbus_t *page_mbus_user = pt_calloc(1, sizeof(pt_ui_page_mbus_t));
    page_mbus_user->topic_id = topic_id;
    page_mbus_user->cbk = cbk;
    page_mbus_user->user_data = user_data;
    pt_list_insert_before(&pt_ui_page_subscribers, &page_mbus_user->node);

    pt_mbus_subscribe(topic_id, cbk, user_data);

    PT_LOGI("[pt_ui_page_subscribe]: 0x%02x 0x%08x", topic_id, cbk);
}

bool pt_ui_page_is_top_by_op(const struct pt_ui_page_operation *op)
{
    if(pt_list_isempty(&pt_ui_pages)) {
        PT_LOGW("[pt_ui_page_on_top]: page empty");
        return false;
    }

    struct pt_ui_page_info *page = pt_container_of(pt_ui_pages.prev, struct pt_ui_page_info, node);

    return page->op == op;
}

bool pt_ui_page_is_top_by_id(uint32_t id)
{
    struct pt_ui_page_info *page = pt_container_of(pt_ui_pages.prev, struct pt_ui_page_info, node);

    return page->page_uid == id;
}

bool pt_ui_page_instack(uint32_t id)
{
    pt_list_t *pos, *n;

    pt_list_for_each_safe(pos, n, &pt_ui_pages) {
        struct pt_ui_page_info *page = pt_container_of(pos, struct pt_ui_page_info, node);
        if(page->page_uid == id) {
            return true;
        }
    }

    return false;
}